﻿#ifndef TEST_BASE_H_
#define TEST_BASE_H_

#include <QObject>
#include <QTest>
#include <functional>
#include "JZProject.h"
#include "JZNodeEngine.h"
#include "JZNodeBuilder.h"
#include "JZNodeValue.h"
#include "JZNodeOperator.h"
#include "JZNodeFunction.h"
#include "JZModule.h"

#define JZBENCHMARK(name) m_benchmark.reset(#name);  \
    while(m_benchmark.run()) \
    for(int bench_idx = 0; bench_idx < m_benchmark.step(); bench_idx++)

class Benchmark
{
public:
    Benchmark();

    inline qint64 step() const { return m_step; }

    void reset(QString name);
    bool run();
    void clear();
    void report();
    void setTestSec(int sec);

protected:
    struct RunInfo
    {
        QString name;
        qint64 count;
        qint64 time;
    };

    QList<RunInfo> m_runInfo;
    qint64 m_stepStart;
    QElapsedTimer m_timer;
    qint64 m_step;
    qint64 m_count;
    bool m_first;
    QString m_name;
    int m_testSec;
};

//JZTestLambda
void JZTestLambda();

class TestModule : public JZModule
{
public:
    virtual void regist(JZScriptEnvironment* env) override;
    virtual void unregist(JZScriptEnvironment* env) override;
};

//TestServer
class TestServer : public QThread
{
    Q_OBJECT

public:
    TestServer();

    void init(JZProject* project);
    void stop();

protected slots:
    void onRuntimeError();

protected:
    virtual void run() override;

    void addInitFunction();
    void addTimeoutFunction();

    JZProject* m_project;
    JZNodeProgram m_program;
    JZNodeEngine* m_engine;
};


//EngineThread
class EngineThread : public QThread
{
public:
    EngineThread();

    void start(QString func, QVariantList input);

    QThread* mainThread;
    JZNodeEngine* engine;
    QString function;
    QVariantList input;
    QVariantList output;

protected:
    void run();
};

//BaseTest
class BaseTest : public QObject
{
    Q_OBJECT

public:
    BaseTest();

    void callTest(QString function);

protected slots:
    void onRuntimeError(JZNodeRuntimeError error);

private slots:
    void initTestCase();
    void init();
    void cleanup();
    
protected:
    struct Promise
    {
        bool ret;
        QVariantList output;
    };

    JZScriptClassItem *makeTestClass();
    QVariant testClassPointer();
    
    bool build();
    bool buildAs(QString code);
    bool call(QString name,const QVariantList &in,QVariantList &out);
    bool callMember(QString name, const QVariantList &in, QVariantList &out);
    void callAsync(QString name,const QVariantList &in);
    void stop();
    void asyncThread(QString name,QVariantList in);  
    void dump(QString dir);
    void msleep(int ms);
    void makeDump();

    virtual void resetTestCase();
    virtual void clearTestCase();
    
    JZProject m_project;
    JZNodeObjectManager *m_objInst;
    JZNodeFunctionManager *m_funcInst;
    JZNodeProgram m_program;
    JZNodeEngine m_engine;
    JZScriptFile *m_file;
    JZNodeBuilder m_builder;
    Promise m_callResult;
    QString m_dumpPath;
    EngineThread m_thread;
};

struct CallArg
{
    int argc;
    QVector<char*> argv;
    QSharedPointer<QByteArrayList> buffers;
};
CallArg genCallUnitArg(QStringList list);

extern std::function<void()> g_testFunc;

#endif